package com.yuntsg.subject_database.controller;


import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.SecureUtil;
import com.yuntsg.subject_database.component.PutNameAndTypeMapComponent;
import com.yuntsg.subject_database.servic.UpDataCorePaperUnite;
import com.yuntsg.subject_database.util.OftenUtils;
import com.yuntsg.subject_database.util.SubDataUtils;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.IOException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@RestController
public class UnifiedSearchController {

//    @Resource
//    ElasticsearchTemplate<CorePaperUnite, String> upDataTable;
    @Resource
    private UpDataCorePaperUnite upDataCorePaperUnite;
    @Resource
    private PutNameAndTypeMapComponent nameAndTypeMap;

    // 字段限定检索，此时输入的是标准的字段名称和检索内容，原来可能是括号或逻辑运算符，因为可能出现在引号内或字段限定前，程序先让其逻辑功能失效，现进行检索了，再给予还原，使其作为普通的字符进行检索。
//    private static QueryBuilder fieldSearch(String value, String field, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap) {
//        System.out.println("\033[31;1m" + "~~~fieldSearch~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        value = value.replace("⟪", "(").replace("⟫", ")").replaceAll(" +", " ").trim();
//        if (field.startsWith("[") && field.endsWith("]")) {
//            field = field.substring("[".length(), field.length() - "]".length());
//        }
//        if (field.equals("[is]") || field.equals("is")) {
//            field = "issue";
//        }
//        System.out.println("field: " + field + " value: " + value);
//        String percentage = getPercentage(value);
//        System.out.println("percentage:2 " + percentage + " length:2 " + value.length());
//        if (field.equals("ti")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_标题-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "ti", percentage);
//        }
//        if (field.equals("tiab")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_标题摘要-- " + "field: " + field + " value: " + value);
//            BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//            queryBuilders.should(changeFieldSearch("ti", value, "ti", percentage));
//            queryBuilders.should(changeFieldSearch("ab", value, "ab", percentage));
//            return queryBuilders;
//        }
//        if (field.equals("kw")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_关键词-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "kw", "100%");
//        }
//        if (field.equals("ab")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_摘要-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "ab", percentage);
//        }
//        if (field.equals("ad")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_地址-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "ad", percentage);
//        }
//        if (field.equals("fau")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_第一作者-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "fau", "100%");
//        }
//        if (field.equals("cau")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_第一通讯作者-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "cau", "100%");
//        }
//        if (field.equals("au")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_作者-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "au", "100%");
//        }
//        if (field.equals("ta")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_期刊-- " + "field: " + field + " value: " + value);
//            return changeFieldSearch(field, value, "ta", "100%");
//        }
//        if (field.equals("unit")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_单位-- " + "field: " + field + " value: " + value);
//            return containsKeySearch(value, nameAndTypeMap, field, "100%", "unit");
//        }
//        if (field.equals("dept")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_科室-- " + "field: " + field + " value: " + value);
//            return containsKeySearch(value, nameAndTypeMap, field, "100%", "dept");
//        }
//        if (field.equals("fund")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_基金-- " + "field: " + field + " value: " + value);
//            return containsKeySearch(value, nameAndTypeMap, field, "100%", "fund");
//        }
//        if (field.equals("ts")) {
//            System.out.println(format.format(new Date()) + " --字段限定检索_主题-- " + "field: " + field + " value: " + value);
//            System.out.println("ts: " + value);
//            return topicSearch(value);
//        }
//        if (field.equals("cnab") || field.equals("cnad") || field.equals("cnau") || field.equals("cncau") || field.equals("cnfau") || field.equals("cnkw") || field.equals("cnta") || field.equals("cnti") || field.equals("cnts") || field.equals("cndept") ||
//                field.equals("enab") || field.equals("enad") || field.equals("enau") || field.equals("encau") || field.equals("enfau") || field.equals("enkw") || field.equals("enta") || field.equals("enti") || field.equals("endept") ||
//                field.equals("ents") || field.equals("mesh") || field.equals("mh") || field.equals("sh") || field.equals("enunit") || field.equals("cnunit") || field.equals("cnfund") || field.equals("enfund")) { //这里的是分词，23种，不分词9种，合计检索字段32种。
//            if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 2) { // 如果用户加了引号，则精确检索，即下面加".keyword"。如果不加引号，则模糊检索，使用参数percentage匹配度。
//                System.out.println(format.format(new Date()) + " --字段限定检索_引号精确检索-- " + "field: " + field + " value: " + value);
//                return QueryBuilders.termQuery(field + ".keyword", value.substring(1, value.length() - 1));
//            }
//            if ((value.contains("?") || value.contains("*")) && value.length() > 2) { // 截词检索，截词检索只能在实体类设置为分词的情况下实现 02.17确定作者是分词，可以截词检索 语种是精确，不能截词检索
//                System.out.println(format.format(new Date()) + " --字段限定检索_截词检索-- " + "field: " + field + " value: " + value);
//                return QueryBuilders.wildcardQuery(field + ".keyword", value);
//            }
//            if (field.equals("mh") && meshAndTermsMap.containsKey(value)) {
//                System.out.println(format.format(new Date()) + " --字段限定检索_主题词扩展检索_精确-- " + "field: " + field + " value: " + value);
//                Set<String> termsSet = meshAndTermsMap.get(value);
//                BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//                for (String terms : termsSet) {
//                    queryBuilders.should(QueryBuilders.matchQuery("enkw", terms).minimumShouldMatch(getPercentage(terms)));
//                    queryBuilders.should(QueryBuilders.matchQuery("enti", terms).minimumShouldMatch(getPercentage(terms)));
//                }
//                return queryBuilders;
//            }
//
//
//            System.out.println(format.format(new Date()) + " --字段限定检索_分词检索-- " + "field: " + field + " value: " + value + " percentage: " + percentage);
//            if (field.equals("cnkw") || field.equals("cnfau") || field.equals("cncau") || field.equals("cnau") || field.equals("cnta") || field.equals("cnunit") || field.equals("cndept") || field.equals("cnfund") ||
//                    field.equals("enkw") || field.equals("enfau") || field.equals("encau") || field.equals("enau") || field.equals("enta") || field.equals("enunit") || field.equals("endept") || field.equals("enfund")) {
//                return QueryBuilders.matchQuery(field, value).minimumShouldMatch("100%");
//            }
//            return QueryBuilders.matchQuery(field, value).minimumShouldMatch(percentage); // 这里的是分词，即模糊检索，percentage为百分比，即模糊的百分比，百分比越小，要求越低，检索出来的越多。
//        } else {
//            System.out.println(format.format(new Date()) + " --字段限定检索_精确检索-- " + "field: " + field + " value: " + value);
//            if (field.equals("py")) {
//                if (!NumberUtil.isInteger(value) && value.length() > 3) {
//                    String temp = value.substring(value.length() - 4);
//                    if (NumberUtil.isInteger(temp)) {
//                        return QueryBuilders.termQuery(field, temp);
//                    }
//                }
//                if (NumberUtil.isInteger(value)) {
//                    return QueryBuilders.termQuery(field, value);
//                }
//            }
//            if (field.equals("pg") || field.equals("vl") || field.equals("issue")) {
//                String temp = value.replace("\"", " ").replace("-", " ").replace(";", " ").replace(",", " ").replace(".", " ").replaceAll(" +", " ").trim();
//                String[] split = temp.split(" ");
//                BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//                for (String s : split) {
//                    if (s.length() > 0) {
//                        queryBuilders.must(QueryBuilders.termQuery(field, s));
//                    }
//                }
//                return queryBuilders;
//            }
//            return QueryBuilders.termQuery(field, value); // 这里的是不分词，即精确检索，有9种，分别是doi、dt、issue、la、pg、pmid、py、sn、vl。其中，issue检索时可以使用is进行检索。
//        }
//    }

    // 有标准的Key检索，如果nameAndTypeMap包含用户输入的检索词，则获取其标准的名称，进行检索，如果不包含，仍使用普通的检索方法。
//    private static QueryBuilder containsKeySearch(String value, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, String field, String percentage, String type) {
//        System.out.println("\033[31;1m" + "~~~containsKeySearch~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        String name = SubDataUtils.reRemaining(value);
//        String md5Name = "";
//        if (name.length() > 16) {
//            md5Name = SecureUtil.md5(name).substring(8, 24).toLowerCase();
//        }
//        if (nameAndTypeMap.containsKey(name) || (md5Name.length() > 0 && nameAndTypeMap.containsKey(md5Name))) {
//            Set<SubDataUtils.StandardNameAndType> nameAndTypeSet = new HashSet<>();
//            if (name.length() > 16) {
//                nameAndTypeSet = nameAndTypeMap.get(md5Name);
//            } else {
//                nameAndTypeSet = nameAndTypeMap.get(name);
//            }
//            for (SubDataUtils.StandardNameAndType nameAndType : nameAndTypeSet) {
//                if (nameAndType.getType().equals(type)) {
//                    String standard = SubDataUtils.getStandardName(nameAndType.getStandard(), name);
//                    System.out.println(format.format(new Date()) + " --字段限定检索_标准名称检索-- " + "field: " + field + " value: " + standard + " type: " + type);
//                    return changeFieldSearch(field, "\"" + standard + "\"", type, percentage);
//                }
//            }
//        }
//        System.out.println(format.format(new Date()) + " --字段限定检索_无逻辑检索-- " + "field: " + field + " value: " + value);
//        return changeFieldSearch(field, value, type, percentage);
//    }

    // 主题检索，对于主题检索，合并了8种，它们之间的逻辑关系是 OR ，即增加了检索结果量。
//    private static QueryBuilder topicSearch(String value) {
//        System.out.println("\033[31;1m" + "~~~topicSearch~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        System.out.println(format.format(new Date()) + " --主题检索-- " + " value: " + value);
//        value = value.replace("⟪", "(").replace("⟫", ")").replaceAll(" +", " ").trim();
//        String percentage = getPercentage(value);
//        BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//        queryBuilders.should(changeFieldSearch("ti", value, "ti", percentage));
//        queryBuilders.should(changeFieldSearch("kw", value, "kw", percentage));
//        queryBuilders.should(changeFieldSearch("ab", value, "ab", percentage));
//        queryBuilders.should(changeFieldSearch("ad", value, "ad", percentage));
//        queryBuilders.should(changeFieldSearch("au", value, "au", percentage));
//        queryBuilders.should(changeFieldSearch("ta", value, "ta", percentage));
//        queryBuilders.should(changeFieldSearch("dept", value, "dept", percentage));
//        queryBuilders.should(changeFieldSearch("fund", value, "fund", percentage));
//        queryBuilders.should(changeFieldSearch("unit", value, "unit", percentage));
//        queryBuilders.should(QueryBuilders.termQuery("doi", value));
//        queryBuilders.should(QueryBuilders.termQuery("pmid", value));
//        return queryBuilders;
//    }

    // 支持的检索字段：ab、ad、au、cau、fau、kw、ta、ti、ts、dept、doi、dt、fund、is、la、mesh、mh、pg、pmid、py、sh、sn、vl，合计32种。
//    private static QueryBuilder getBoolQueryBuilder(String str, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, Set<String> fieldSet, List<String> details) {
//        System.out.println("\033[31;1m" + "~~~getBoolQueryBuilder~~~" + "\033[0m");
//        str = str.replace("[is]", "[issue]");
////        if (str.indexOf("[") > 0 && str.indexOf("]") > 0 && str.indexOf("]") > str.indexOf("[") && str.endsWith("]") && str.split("\\[").length == 2 && str.split("]").length == 1) {
////            str = str.replace("[is]", "[issue]"); // 因检索字段[is]是保留字段，实体类中实际上是[issue]，因此，需要转换一下。
////            String value = str.substring(0, str.indexOf("[")).trim();
////            String field = str.substring(str.indexOf("[") + 1, str.length() - "]".length());
////            return fieldSearch(value, field, nameAndTypeMap, meshAndTermsMap);
////        }
//        return NoLogicTxtSearch(str, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//    }

    // 无逻辑文本、无括号检索，如果有引号，直接按主题检索，如果有字段限定，先提取字段限定，余下的按自由词检索，如果不含字段限定，最后按自由词检索。
//    private static QueryBuilder NoLogicTxtSearch(String str, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, Set<String> fieldSet, List<String> details) {
//        System.out.println("\033[31;1m" + "~~~NoLogicTxtSearch~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        System.out.println(format.format(new Date()) + " --进入无逻辑运算检索-- " + " str: " + str);
//        details.add("进入无逻辑运算检索");
//        if (str.startsWith("\"") && str.endsWith("\"") && !isFieldLimit(str)) {
//            return topicSearch(SubDataUtils.reStopWord(str));
//        }
//        if (isFieldLimit(str)) {
//            Set<SubDataUtils.FieldAndValue> extractFieldSet = new HashSet<>();
//            System.out.println(format.format(new Date()) + " --含有字段限定，对提取到的字段进行检索1-- " + " str: " + str + " extractFieldSet: " + extractFieldSet);
//            String remaining = SubDataUtils.fieldFrontExtract(str, fieldSet, extractFieldSet, nameAndTypeMap);
//            System.out.println(format.format(new Date()) + " --含有字段限定，对提取到的字段进行检索2-- " + " remaining: " + remaining + " extractFieldSet: " + extractFieldSet);
//
//            for (SubDataUtils.FieldAndValue fieldAndValue : extractFieldSet) {
//                details.add("field=" + fieldAndValue.getField() + ", value=" + fieldAndValue.getValue());
//            }
//            details.add("remaining: " + remaining);
//            BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//            if (extractFieldSet.size() > 0) {
//                for (SubDataUtils.FieldAndValue fieldAndValue : extractFieldSet) {
//                    String field = fieldAndValue.getField();
//                    String value = fieldAndValue.getValue();
//                    queryBuilders.must(fieldSearch(value, field, nameAndTypeMap, meshAndTermsMap));
//                }
//            }
//            if (remaining.length() > 1) {
//                queryBuilders.must(freeWordSearch(remaining, nameAndTypeMap, meshAndTermsMap, details));
//            }
//            return queryBuilders;
//        }
//        return freeWordSearch(str, nameAndTypeMap, meshAndTermsMap, details);
//    }

    // 对自由文本的处理，此时已经没有了字段限定，没有了逻辑运算符。提取出来的字段限定之间是 OR 运算，与余下来的词之间是 AND 运算，且余下的词按 TS 主题检索。
//    private static QueryBuilder freeWordSearch(String str, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, List<String> details) {
//        System.out.println("\033[31;1m" + "~~~freeWordSearch~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        Set<SubDataUtils.FieldAndValue> extractFieldSet = new HashSet<>();
//        String remaining = SubDataUtils.doExtractField(str, nameAndTypeMap, extractFieldSet, "", "");
//        System.out.println(format.format(new Date()) + " --自由文本检索_提取检索-- " + " str: " + str + " extractFieldSet: " + extractFieldSet + " remaining: " + remaining);
//        BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//        if (extractFieldSet.size() > 0) {
//            queryBuilders.must(getExtractFieldSet(extractFieldSet, nameAndTypeMap, meshAndTermsMap));
//        }
//        if (remaining.length() > 1) {
//            String[] list = remaining.split(" ");
//            for (String s : list) {
//                if (s.startsWith("doi:")) {
//                    s = s.substring("doi:".length()).trim();
//                }
//                s = SubDataUtils.reEndStr(s);
//                if (s.length() > 3) {
//                    queryBuilders.must(topicSearch(SubDataUtils.reStopWord(s)));
//                }
//            }
//        }
//
//        for (SubDataUtils.FieldAndValue fieldAndValue : extractFieldSet) {
//            details.add("field=" + fieldAndValue.getField() + ", value=" + fieldAndValue.getValue());
//        }
//        details.add("remaining: " + remaining);
//
//        System.out.println(format.format(new Date()) + " --自由文本检索-- " + " str: " + str + " extractFieldSet: " + extractFieldSet + " remaining: " + remaining);
//        return queryBuilders;
//    }

    // 如果同一个字符串，有两个或以上的属性（比如，同一个字符串既是作者，又是关键词），它们内部的逻辑关系为 OR 运算，与其它的之间还是 AND 运算。
//    private static QueryBuilder getExtractFieldSet(Set<SubDataUtils.FieldAndValue> extractFieldSet, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap) {
//        System.out.println("\033[31;1m" + "~~~getExtractFieldSet~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        System.out.println(format.format(new Date()) + " --对提取出来的含有字段限定的部分进行检索-- " + " extractFieldSet: " + extractFieldSet);
//        Map<String, Set<String>> valueAndFieldSetMap = new HashMap<>();
//        for (SubDataUtils.FieldAndValue fieldAndValue : extractFieldSet) {
//            OftenUtils.putStrSetStr(fieldAndValue.getValue(), fieldAndValue.getField(), valueAndFieldSetMap);
//        }
//
//        BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//        for (Map.Entry<String, Set<String>> entry : valueAndFieldSetMap.entrySet()) {
//            Set<String> FieldSet = entry.getValue();
//            String value = entry.getKey().replace("⟪", "(").replace("⟫", ")").replaceAll(" +", " ").trim();
//            if (value.length() > 0) {
//                if (FieldSet.size() == 1) {
//                    for (String field : FieldSet) {
//                        queryBuilders.must(fieldSearch(value, field, nameAndTypeMap, meshAndTermsMap));
//                    }
//                } else {
//                    if (FieldSet.size() > 1) {
//                        // 如果相同的一个字符串，有多个属性，如果其field为"enkw"或"mh"，则执行扩大到"enti"检索。
//                        BoolQueryBuilder or = new BoolQueryBuilder();
//                        for (String field : FieldSet) {
//                            if (field.equals("enkw") || field.equals("mh")) {
//                                or.should(QueryBuilders.matchQuery("enti", value).minimumShouldMatch(getPercentage(value)));
//                            }
//                            or.should(fieldSearch(value, field, nameAndTypeMap, meshAndTermsMap));
//                        }
//                        queryBuilders.must(or);
//                    }
//                }
//            }
//        }
//        return queryBuilders;
//    }

    // 对于区分中、英文的字段，给予合并检索（OR关系），共八种。
//    private static QueryBuilder changeFieldSearch(String field, String value, String str, String percentage) {
//        System.out.println("\033[31;1m" + "~~~changeFieldSearch~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        System.out.println(format.format(new Date()) + " --添加cn、en检索-- " + " field: " + field + " value: " + value);
//        String fieldCn = "cn" + str;
//        String fieldEn = "en" + str;
//        value = value.replace("⟪", "(").replace("⟫", ")").replaceAll(" +", " ").trim();
//        if (field.equals(str)) {
//            BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//            if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 2) { // 如果用户加了引号，则精确检索，即下面加".keyword"（上面的13个在建实体类时，为text_type分词）。如果不加引号，则模糊检索，默认100%匹配，如果字符串的长度大于15个，90%，大于35个则80%。
//                queryBuilders.should(QueryBuilders.termQuery(fieldCn + ".keyword", value.substring(1, value.length() - 1)));
//                queryBuilders.should(QueryBuilders.termQuery(fieldEn + ".keyword", value.substring(1, value.length() - 1)));
//                return queryBuilders;
//            }
//            if (value.contains("?") || value.contains("*")) { // 截词检索，截词检索只能在实体类设置为分词的情况下实现
//                queryBuilders.should(QueryBuilders.wildcardQuery(fieldCn + ".keyword", value));
//                queryBuilders.should(QueryBuilders.wildcardQuery(fieldEn + ".keyword", value));
//                return queryBuilders;
//            }
//            return QueryBuilders.multiMatchQuery(value, fieldCn, fieldEn).minimumShouldMatch(percentage);
//        } else {
//            return QueryBuilders.matchQuery(field, value).minimumShouldMatch(percentage); // 这里的是分词，即模糊检索，percentage为百分比，即模糊的百分比，百分比越小，要求越低，检索出来的越多。
//        }
//    }

    //比如，原始最大90，原始最小65，想要最大100，想要最小1，实际81，则：x=((实际-原始最小)*想要最大+(原始最大-实际)*想要最小)/(原始最大-原始最小)
    private static String getPercentage(String str) {
        int len = str.length();
        int max = 60;// 设置允许的最大误差为60，如果再低（低于60）检索的不相关的就太多了。
        if (OftenUtils.isContainChinese(str)) {
            if (len < 8) {
                return "99%";
            }
            if (len < 52) {
                return ((len - 8) * max + (52 - len) * 100) / (52 - 8) + "%";
            }
            return max + "%";
        } else {
            if (len < 11) {
                return "99%";
            }
            if (len < 102) {
                return ((len - 11) * max + (102 - len) * 100) / (102 - 11) + "%";
            }
            return max + "%";
        }
    }

//    private static void addBoolQueryBuilder(BoolQueryBuilder father, QueryBuilder child, String code) {
//        System.out.println("\033[31;1m" + "~~~addBoolQueryBuilder~~~" + "\033[0m");
//        if (code.equals("⬆not⬇")) {
//            father.mustNot(child);
//        }
//        if (code.equals("⬆and⬇")) {
//            father.must(child);
//        }
//        if (code.equals("⬆oor⬇")) {
//            father.should(child);
//        }
//    }

    // J Neurol[TA]-> value: J Neurol field: TA     therapy[SH] OR blood[SH]
//    private static String doSearch(String front, String code, String after, Map<String, BoolQueryBuilder> countMap, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, Set<String> fieldSet, List<String> details) {
//        System.out.println("\033[31;1m" + "~~~doSearch~~~" + "\033[0m");
//        BoolQueryBuilder father = new BoolQueryBuilder();
//        if (code.equals("⬆oor⬇")) {
//            if (countMap.containsKey(front)) {
//                father.should(countMap.get(front));
//            } else {
//                father.should(getBoolQueryBuilder(front, nameAndTypeMap, meshAndTermsMap, fieldSet, details));
//            }
//        } else {
//            if (countMap.containsKey(front)) {
//                father.must(countMap.get(front));
//            } else {
//                father.must(getBoolQueryBuilder(front, nameAndTypeMap, meshAndTermsMap, fieldSet, details));
//            }
//        }
//        if (countMap.containsKey(after)) {
//            addBoolQueryBuilder(father, countMap.get(after), code);
//        } else {
//            addBoolQueryBuilder(father, getBoolQueryBuilder(after, nameAndTypeMap, meshAndTermsMap, fieldSet, details), code);
//        }
//        String name = "result" + countMap.size() + 1 + "]";
//        countMap.put(name, father);
//        return name;
//    }

    // 执行检索的入口，先括号内的，逻辑关系顺序是NOT->AND->OR，括号的计算完后，再计算有逻辑运算符的（"⬆and⬇"、"⬆oor⬇"、"⬆not⬇"），最后按自由文本来处理。若括号内无逻辑运算符，则将括号替换为"⟪"和"⟫"，即作为普通的括号进入下一步的运算。
//    public static QueryBuilder select(String str, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, Set<String> fieldSet, List<String> details) throws SQLException {
//        System.out.println("\033[31;1m" + "~~~select~~~str: " + str + "\033[0m");
//        Map<String, BoolQueryBuilder> countMap = new HashMap<>();
//        Matcher matcher = Pattern.compile("\\(([^\\(\\)]+)\\)").matcher(str);
//        while (matcher.find()) {
//            String search = matcher.group(1);
//            System.out.println("有括号检索式_前: " + search + " str: " + str);
//            if (search.contains("⬆")) {
//                search = logicalOperation(search, "⬆not⬇", countMap, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//                search = logicalOperation(search, "⬆and⬇", countMap, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//                search = logicalOperation(search, "⬆oor⬇", countMap, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//                str = str.replace("(" + matcher.group(1) + ")", search);
//            } else {
//                str = str.replace("(" + matcher.group(1) + ")", "⟪" + search + "⟫");
//            }
//            System.out.println("有括号检索式_后: " + search + " str: " + str);
//            details.add("有括号检索式_后");
//            details.add(search);
//        }
//        str = str.replace("⟪", "(").replace("⟫", ")").replaceAll(" +", " ").trim();
//        // 无括号检索式
//        System.out.println("无括号检索式_前： " + str);
//        str = logicalOperation(str, "⬆not⬇", countMap, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//        str = logicalOperation(str, "⬆and⬇", countMap, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//        str = logicalOperation(str, "⬆oor⬇", countMap, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//        System.out.println("无括号检索式_后： " + str);
//        details.add("无括号检索式_后");
//        if (countMap.containsKey(str)) {
//            System.out.println("结果输出： " + str);
//            return countMap.get(str);
//        }
//        return NoLogicTxtSearch(str, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//    }

    // 切分两两之间的逻辑检索式，本方法将其切分为前、后两个词，再调用检索方法doSearch，doSearch进行计算后保存进中间结果MAP，并返回MAP的KEY的字符串名称（方便以后get拿结果）。本方法再将原两两检索替换为此字符串返回。
//    private static String logicalOperation(String str, String code, Map<String, BoolQueryBuilder> countMap, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, Set<String> fieldSet, List<String> details) throws SQLException {
//        System.out.println("\033[31;1m" + "~~~logicalOperation~~~" + "\033[0m");
//        while (str.contains(code)) {
//            int codeStart = str.indexOf(code);
//            int codeEnd = codeStart + code.length();
//            String front = "";
//            for (int i = codeStart; i > 0; i--) {
//                if (str.substring(i, codeStart).startsWith("⬆and⬇") || str.substring(i, codeStart).startsWith("⬆not⬇") || str.substring(i, codeStart).startsWith("⬆oor⬇")) {
//                    front = str.substring(i + 5, codeStart);//因为"⬆and⬇"、"⬆not⬇"、"⬆oor⬇"的长度均为5，故直接写死为5。
//                    break;
//                }
//            }
//            if (front.equals("")) {
//                front = str.substring(0, codeStart);
//            }
//            String after = "";
//            for (int j = codeEnd; j < str.length(); j++) {
//                if (str.length() > j + 5) {//因为"⬆and⬇"、"⬆not⬇"、"⬆oor⬇"的长度均为5，故直接写死为5。
//                    if (str.startsWith("⬆and⬇", j) || str.startsWith("⬆not⬇", j) || str.startsWith("⬆oor⬇", j)) {
//                        after = str.substring(codeEnd, j);
//                        break;
//                    }
//                }
//            }
//            if (after.equals("")) {
//                after = str.substring(codeEnd);
//            }
//            if (front.length() > 0 && after.length() > 0) {
//                System.out.println("front: " + front + " after: " + after);
//                String search = doSearch(front, code, after, countMap, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//                str = str.replace(front + code + after, search);
//                System.out.println("中间检索结果： " + search + " str: " + str);
//            }
//        }
//        return str;
//    }

    // 是否包含字段限定符号
    public static boolean isFieldLimit(String s) {
        return s.contains("[ti]") || s.contains("[kw]") || s.contains("[ts]") || s.contains("[ab]") || s.contains("[ad]") || s.contains("[au]") || s.contains("[ta]") || s.contains("[fau]") || s.contains("[cau]") || s.contains("[unit]") || s.contains("[jid]") || s.contains("[enunit]") ||
                s.contains("[cnunit]") || s.contains("[cnab]") || s.contains("[cnad]") || s.contains("[cnau]") || s.contains("[cncau]") || s.contains("[cnfau]") || s.contains("[cnkw]") || s.contains("[cnta]") || s.contains("[cnti]") || s.contains("[cnts]") || s.contains("[dept]") ||
                s.contains("[doi]") || s.contains("[dt]") || s.contains("[enab]") || s.contains("[enad]") || s.contains("[enau]") || s.contains("[encau]") || s.contains("[enfau]") || s.contains("[enkw]") || s.contains("[enta]") || s.contains("[enti]") || s.contains("[ents]") ||
                s.contains("[fund]") || s.contains("[issue]") || s.contains("[is]") || s.contains("[la]") || s.contains("[mesh]") || s.contains("[mh]") || s.contains("[pg]") || s.contains("[pmid]") || s.contains("[py]") || s.contains("[sh]") || s.contains("[sn]") || s.contains("[vl]") || s.contains("[cndept]")
                || s.contains("[endept]") || s.contains("[cnfund]") || s.contains("[enfund]");
    }

    // 是否为pmid号的集合。
    public static boolean isPmid(String s) {
        s = s.replaceAll(" +", "").replace("0", "").replace("1", "").replace("2", "").replace("3", "").replace("4", "").replace("5", "").replace("6", "")
                .replace("7", "").replace("8", "").replace("9", "").replace("[", "").replace("]", "").replace(".", "").replace(",", "").replace(";", "");
        return s.equals("");
    }

    // 如果用户输入的是多个doi号，则按 OR 运算。
    public static List<String> isDoiList(String str, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, String info) {
        String[] doiS = info.replaceAll(" +", " ").trim().toLowerCase().split(" ");
        for (String s : doiS) {
            if (s.startsWith("doi:")) {
                s = s.substring("doi:".length()).trim();
            }
            if (!s.startsWith("10.")) {
                return new ArrayList<>();
            }
        }

        List<String> list = new ArrayList<>();
        str = str.replace("纃i 10 纃c ", "10 纃c ").replace("纃d 10 纃c ", "10 纃c ").replace("纃f 10 纃c ", "10 纃c ").replace("纃g 10 纃c ", "10 纃c ")
                .replace("纃i doi 纃x", "").replace("纃d doi 纃x", "").replace("纃f doi 纃x", "").replace("纃g doi 纃x", "")
                .replace("纃i doi 纃v", "").replace("纃d doi 纃v", "").replace("纃f doi 纃v", "").replace("纃g doi 纃v", "")
                .toLowerCase().replaceAll(" +", " ").trim();
        String[] split = str.split("10 纃c ");
        for (String doi : split) {
            doi = doi.trim();
            if (doi.endsWith("doi 纃x")) {
                doi = doi.substring(0, doi.length() - "doi 纃x".length()).trim();
            }
            if (doi.endsWith("doi 纃v")) {
                doi = doi.substring(0, doi.length() - "doi 纃v".length()).trim();
            }
            String temp = "10 纃c " + doi;
            String md5Name = "";
            if (temp.length() > 16) {
                md5Name = SecureUtil.md5(temp).substring(8, 24).toLowerCase();
            }
            if (nameAndTypeMap.containsKey(temp) || (md5Name.length() > 0 && nameAndTypeMap.containsKey(md5Name))) {
                Set<SubDataUtils.StandardNameAndType> nameAndTypeSet = new HashSet<>();
                if (temp.length() > 16) {
                    nameAndTypeSet = nameAndTypeMap.get(md5Name);
                } else {
                    nameAndTypeSet = nameAndTypeMap.get(temp);
                }
                for (SubDataUtils.StandardNameAndType standardNameAndType : nameAndTypeSet) {
                    String standard = SubDataUtils.getStandardName(standardNameAndType.getStandard(), temp);
                    String type = standardNameAndType.getType();
                    if (type.equals("doi")) {
                        list.add(standard);
                    }
                }
            }
        }
        return list;
    }

//    @RequestMapping("/update")  // http://localhost:16898/update
//    public String update() {
//        try {
//            upDataCorePaperUnite.main();
//        } catch (IOException | SQLException e) {
//            e.printStackTrace();
//        }
//        return "ok!";
//    }

//    @GetMapping("/get")  // http://localhost:16898/get
//    public List<SearchResult> get(String query) throws Exception {
//
//        String field = "unit";
//        String value = "延安大)学生命(科学学院";
//
//        Set<String> fieldSet = SubDataUtils.getFieldSet();
//        System.out.println(query);
//        List<String> details = new ArrayList<>();
//
////        QueryBuilder select = select(query, nameAndTypeMap); // 单个的测试
//        QueryBuilder select = assign(query, nameAndTypeMap.getNameAndTypeMap(), nameAndTypeMap.getMeshAndTermsMap(), fieldSet, details); // 指派模式
//        System.out.println("nameAndTypeMap.getNameAndTypeMap(): " + nameAndTypeMap.getNameAndTypeMap().size());
//
//
//        /**
//         * 每页返回数量
//         */
//        PageSortHighLight psh = new PageSortHighLight(1, 10000);
//
//        /**
//         * 字段高亮
//         */
//        psh.setHighLight(new HighLight().field("title"));
//
//        /**
//         * 返回字段
//         */
//        String[] includes = {"id", "enunit", "cnunit", "cnau", "cnkw", "cnti", "dept", "enau", "enkw", "enti", "articleid", "jour"};
//
//        Attach attach = new Attach();
//        attach.setPageSortHighLight(psh);
//        attach.setIncludes(includes);
//
//        PageList<CorePaperUnite> page = upDataTable.search(select, attach, CorePaperUnite.class);
//
//        List<SearchResult> list = new ArrayList<>();
//        for (CorePaperUnite corePaperUnite : page.getList()) {
//            SearchResult searchResult = BeanUtil.copyProperties(corePaperUnite, SearchResult.class);
//            searchResult.setDetails(details);
//            list.add(searchResult);
//        }
//
//        return list;
//
//    }

    /**
     * 1、对原始字符串转小写，如果是标题、期刊、doi、pmid、作者、关键词等，直接处理，包括多个pmid、多个doi的情况；
     * 2、仍使用原始字符串，对" AND "、" OR "、" NOT "分别替换为"⬆and⬇"、"⬆oor⬇"、"⬆not⬇"，防止转小写后无法识别是逻辑运算符， "⬆⬇"表示是逻辑运算符。处理前需要对【】内的空格使用正则给予删除，最后转为小写。
     * 3、引号内的括号、逻辑运算符进行替换，使之失效。"("、")"、"⬆and⬇"、"⬆oor⬇"、"⬆not⬇"分别替换为"⟪"、"⟫"、" and "、" oor "、" not "，对于引号一直不处理。
     * 4、标准字段限定前的括号进行替换，这种要求必须是成对出现（且不含字段限定、不含逻辑运算符）的才可以（使用正则查找最内层的括号），"("、")"分别替换为"⟪"、"⟫"，单个的或不是成对的仍然保留。处理前需要对【】内的空格使用正则给予删除。
     * "["、"]"在处理字段限定内部的空格时，进行的转换；括号及三个逻辑运算符在引号内部时，进行的转换，"("、")"也在字段限定前，对成对出现的进行了转换，这三个均在最终计算时（分字段限定和自由文本两种），还原。
     * 5、对于自由文本共存在：与字段限定检索共存、与引号共存、仅字段限定、仅引号、仅文本五种，需要五个不同的方法来处理，其中，仅文本的可以供其它调用，引号的以及仅文本的分解后仍不是字段限定的字符串，走[ts]主题检索。
     * 6、对于mh的进行扩展检索，即对所有的款目词进行 OR 运算。对于自由文本，提取出来一个以上的属性（比如，同时属于作者和关键词），则它们之间是 OR 关系，与其它之间仍然是 AND 关系。
     * 7、整个处理过程是：1、先分解括号（使用正则查找最内层的括号）；2、再处理逻辑运算符；3、最后是自由文本（四种情况）。凡没有括号，没有逻辑运算符的，按自由文本处理。
     * 8、基本检索单元，分三种，一是字段限定检索，二是主题检索，三是加cn、en检索。其它检索均是在这个基础上的，比如，自由文本，就是先提取出字段，再加上主题检索。
     */

    // 接受检索的第一站
//    public QueryBuilder assign(String info, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, Set<String> fieldSet, List<String> details) throws Exception {
//        info = info.replace("[title/abstract]", "[tiab]").replace("[Title/Abstract]", "[tiab]");
//        System.out.println("\033[31;1m" + "~~~assign~~~" + "\033[0m");
//        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        String search = SubDataUtils.reRemaining(info);
//        // 如果是标题、作者、期刊等
//        System.out.println(format.format(new Date()) + " search: " + search + " info: " + info);
//        String md5Name = "";
//        if (search.length() > 16) {
//            md5Name = SecureUtil.md5(search).substring(8, 24).toLowerCase();
//        }
//        if (nameAndTypeMap.containsKey(search) || (md5Name.length() > 0 && nameAndTypeMap.containsKey(md5Name))) {
//            details.add("进入是否为作者或期刊");
//            System.out.println(format.format(new Date()) + " --01-- 进入是否为作者或期刊 search: " + search + " info: " + info);
//            BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//            Set<SubDataUtils.StandardNameAndType> nameAndTypeSet = new HashSet<>();
//            if (search.length() > 16) {
//                nameAndTypeSet = nameAndTypeMap.get(md5Name);
//            } else {
//                nameAndTypeSet = nameAndTypeMap.get(search);
//            }
//            for (SubDataUtils.StandardNameAndType standardNameAndType : nameAndTypeSet) {
//                String standard = SubDataUtils.getStandardName(standardNameAndType.getStandard(), search);
//                String type = standardNameAndType.getType();
//                System.out.println("search: " + search + " standard: " + standard + " type: " + type);
//                if (standard.length() > 0) {
//                    queryBuilders.should(getBoolQueryBuilder(standard + "[" + type + "]", nameAndTypeMap, meshAndTermsMap, fieldSet, details));
//                } else {
//                    queryBuilders.should(getBoolQueryBuilder(search + "[" + type + "]", nameAndTypeMap, meshAndTermsMap, fieldSet, details));
//                }
//            }
//            return queryBuilders;
//        }
//        // 如果是一个或多个pmid
//        if (isPmid(info)) {
//            System.out.println(format.format(new Date()) + " --03-- 进入是否为pmid");
//            details.add("进入是否为pmid");
//            BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//            String str = info.replace(".", " ").replace(",", " ").replace(";", " ").replace("[", " ").replace("]", " ").replaceAll(" +", " ").trim();
//            String[] list = str.split(" ");
//            for (String pmid : list) {
//                if (NumberUtil.isInteger(pmid)) {
//                    queryBuilders.should(QueryBuilders.termQuery("pmid", pmid));
//                }
//            }
//            return queryBuilders;
//        }
//        // 如果用户输入的是多个doi的集合，则按 OR 运算后输出。
//        List<String> doiList = isDoiList(search, nameAndTypeMap, info);
//        System.out.println("doiList: " + doiList);
//        if (doiList.size() > 1) {
//            details.add("进入是否为多个doi号");
//            BoolQueryBuilder queryBuilders = new BoolQueryBuilder();
//            for (String doi : doiList) {
//                queryBuilders.should(fieldSearch(doi, "doi", nameAndTypeMap, meshAndTermsMap));
//            }
//            return queryBuilders;
//        }
//
//        System.out.println("result:a " + info);
//        String result = SubDataUtils.reFreeTxt(info);
//        System.out.println(format.format(new Date()) + " --05-- 进入自由文本检索a result: " + result);
//        return select(result, nameAndTypeMap, meshAndTermsMap, fieldSet, details);
//    }


}
